package cn.catsdad.base.service.impl;

import cn.catsdad.base.entity.BaseEntity;
import cn.catsdad.base.page.PageBean;
import cn.catsdad.base.page.PageParam;
import cn.catsdad.base.processor.IBaseServiceProcessor;
import cn.catsdad.base.repository.api.IBaseRepository;
import cn.catsdad.base.repository.api.ICommonRepository;
import cn.catsdad.base.service.api.IBaseService;
import cn.catsdad.constant.iConstant.ICommonWarningType;
import cn.catsdad.util.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

/**
 * service最上层实现
 *
 * @author xuyl
 * @version V1.0.0 2024/7/6
 * @since V100R001
 */
public abstract class BaseServiceImpl<T extends BaseEntity, ID extends Serializable, R extends IBaseRepository<T, ID>>
        implements IBaseService<T, ID> {

    private Map<String, IBaseServiceProcessor<T, ID>> processorMap = new HashMap<>();
    protected R repository;
    protected ICommonRepository<T> commonRepository;

    @Override
    @Transactional
    public T save(T entity) {
        List<IBaseServiceProcessor<T, ID>> practicalProcessor = getPracticalProcessor();
        practicalProcessor.forEach(p -> p.preSave(Collections.singleton(entity)));
        repository.save(entity);
        practicalProcessor.forEach(p -> p.postSave(Collections.singleton(entity)));
        return entity;
    }

    @Override
    @Transactional
    public void saveAll(Collection<T> entities) {
        List<IBaseServiceProcessor<T, ID>> practicalProcessor = getPracticalProcessor();
        practicalProcessor.forEach(p -> p.preSave(entities));
        repository.saveAll(entities);
        practicalProcessor.forEach(p -> p.postSave(entities));
    }

    @Override
    public T update(T entity) {
        List<IBaseServiceProcessor<T, ID>> practicalProcessor = getPracticalProcessor();
        practicalProcessor.forEach(p -> p.preUpdate(Collections.singleton(entity)));
        repository.save(entity);
        practicalProcessor.forEach(p -> p.postUpdate(Collections.singleton(entity)));
        return entity;
    }

    @Override
    public void updateAll(Collection<T> entities) {
        List<IBaseServiceProcessor<T, ID>> practicalProcessor = getPracticalProcessor();
        practicalProcessor.forEach(p -> p.preUpdate(entities));
        repository.saveAll(entities);
        practicalProcessor.forEach(p -> p.postUpdate(entities));
    }

    @Override
    @Transactional
    public void deleteById(ID id) {
        List<IBaseServiceProcessor<T, ID>> practicalProcessor = getPracticalProcessor();
        practicalProcessor.forEach(p -> p.preDelete(Collections.singleton(id)));
        repository.deleteById(id);
        practicalProcessor.forEach(p -> p.postDelete(Collections.singleton(id), repository.getEntityClass()));
    }

    @Override
    @Transactional
    public void deleteByIds(Collection<ID> ids) {
        List<IBaseServiceProcessor<T, ID>> practicalProcessor = getPracticalProcessor();
        practicalProcessor.forEach(p -> p.preDelete(ids));
        repository.deleteAllById(ids);
        practicalProcessor.forEach(p -> p.postDelete(ids, repository.getEntityClass()));
    }

    @Override
    @Transactional
    public void deleteByEntity(T entity) {
        List<IBaseServiceProcessor<T, ID>> practicalProcessor = getPracticalProcessor();
        practicalProcessor.forEach(p -> p.preEntityDeleted(Collections.singleton(entity)));
        repository.delete(entity);
        practicalProcessor.forEach(p -> p.postEntityDelete(Collections.singleton(entity)));
    }

    @Override
    @Transactional
    public void deleteByEntities(Collection<T> entities) {
        List<IBaseServiceProcessor<T, ID>> practicalProcessor = getPracticalProcessor();
        practicalProcessor.forEach(p -> p.preEntityDeleted(entities));
        repository.deleteAll(entities);
        practicalProcessor.forEach(p -> p.postEntityDelete(entities));
    }

    @Override
    public void pageQuery(PageBean<T> pageBean, PageParam pageParam) {
        List<IBaseServiceProcessor<T, ID>> practicalProcessor = getPracticalProcessor();
        practicalProcessor.forEach(p -> p.prePageQuery(pageBean, pageParam));
        String query = pageBean.getQuery();
        if (StringUtil.isEmpty(query)) {
            pageBean.setEntityName(repository.getEntityClass().getName() + " " + repository.getQueryAlias() + " ");
            pageBean.setQuery("select " + repository.getQueryAlias() + " from " + pageBean.getEntityName());
        }
        commonRepository.pageQuery(pageBean, pageParam);
        practicalProcessor.forEach(p -> p.postPageQuery(pageBean, pageParam));
    }

    @Override
    public List<T> queryByIds(Collection<ID> ids) {
        return doQueryByIds(ids);
    }

    @Override
    public T queryById(ID id) {
        List<T> list = doQueryByIds(Collections.singleton(id));
        return StringUtil.isNotEmpty(list) ? list.get(0) : null;
    }

    @Override
    @SuppressWarnings(ICommonWarningType.UNCHECKED)
    public Map<ID, T> queryMap(Collection<ID> ids) {
        List<T> dataList = doQueryByIds(ids);
        return dataList.stream().collect(Collectors.toMap(t -> (ID) t.getId(), t -> t, (t1, t2) -> t2));
    }

    @Override
    @Transactional
    public void batchSave(Collection<T> entities) {
        if (StringUtil.isNotEmpty(entities)) {
            List<IBaseServiceProcessor<T, ID>> practicalProcessor = getPracticalProcessor();
            practicalProcessor.forEach(p -> p.preSave(entities));
            repository.batchSave(entities);
            practicalProcessor.forEach(p -> p.postSave(entities));
        }
    }

    @Override
    @Transactional
    public void batchInsert(Collection<T> entities) {
        List<IBaseServiceProcessor<T, ID>> practicalProcessor = getPracticalProcessor();
        practicalProcessor.forEach(p -> p.preInsert(entities));
        repository.batchInsert(entities);
        practicalProcessor.forEach(p -> p.postInsert(entities));
    }

    @Override
    @Transactional
    public void batchUpdate(Collection<T> entities) {
        List<IBaseServiceProcessor<T, ID>> practicalProcessor = getPracticalProcessor();
        practicalProcessor.forEach(p -> p.preUpdate(entities));
        repository.batchUpdate(entities);
        practicalProcessor.forEach(p -> p.postUpdate(entities));
    }


    /**
     * 预留填充冗余字段方法
     *
     * @param entities 实体
     */
    protected void loadTransientFields(Collection<T> entities) {

    }


    /**
     * 根据id查询
     *
     * @param ids id集合
     * @return 查询结果
     */
    private List<T> doQueryByIds(Collection<ID> ids) {
        List<T> result = new ArrayList<>();
        if (StringUtil.isNotEmpty(ids)) {
            List<IBaseServiceProcessor<T, ID>> practicalProcessor = getPracticalProcessor();
            for (IBaseServiceProcessor<T, ID> processor : practicalProcessor) {
                List<T> dataFromCache = processor.preQuery(ids, repository.getEntityClass());
                if (StringUtil.isNotEmpty(dataFromCache)) {
                    result = dataFromCache;
                }
            }
            if (StringUtil.isEmpty(result)) {
                result = repository.findAllById(ids);
            }
            for (IBaseServiceProcessor<T, ID> processor : practicalProcessor) {
                processor.postQuery(result);
            }
            loadTransientFields(result);
        }
        return result;
    }

    /**
     * 获取最后实际生效的processor
     *
     * @return 实际生效的processor
     */
    protected List<IBaseServiceProcessor<T, ID>> getPracticalProcessor() {
        List<IBaseServiceProcessor<T, ID>> result = new ArrayList<>();
        List<String> ignoreProcessAlias = getIgnoreProcessAlias();
        if (StringUtil.isNotEmpty(ignoreProcessAlias)) {
            for (Map.Entry<String, IBaseServiceProcessor<T, ID>> entry : this.processorMap.entrySet()) {
                if (!ignoreProcessAlias.contains(entry.getKey())) {
                    result.add(entry.getValue());
                }
            }
        } else {
            result.addAll(this.processorMap.values());
        }
        return result.stream().sorted(Comparator.comparing(IBaseServiceProcessor::getOrder))
                .collect(Collectors.toList());
    }

    /**
     * 暴漏给子级，可以根据processor别名来剔除指定的processor
     *
     * @return 提出的processor别名list
     */
    protected List<String> getIgnoreProcessAlias() {
        return new ArrayList<>();
    }

    @Autowired
    public void setProcessorMap(Map<String, IBaseServiceProcessor<T, ID>> processorMap) {
        this.processorMap = processorMap;
    }

    @Resource
    public void setRepository(R repository) {
        this.repository = repository;
    }

    @Autowired
    public void setCommonRepository(ICommonRepository<T> commonRepository) {
        this.commonRepository = commonRepository;
    }
}
